// Promise  模擬
// 执行状态
const PENDDING = 'pendding' //等待
const FUILEED = 'fuilleed' //成功
const REJECT = 'reject' //失败

class MyPromise {
    status = PENDDING //对象内的状态初始化
    value = undefined //回调成功初始值
    reason = undefined //回调失败初始值
    successCallback = [] //成功的回调
    failCallback = [] //失败的回调
    constructor(excutor) { //内置生成器，生成一个处理成功和失败的调用函数（resolve,reject）
        try { //执行器错误捕获
            excutor(this.resolve, this.reject)
        } catch (e) {
            this.reject(e)
        }
    }
    resolve = value => { //成功的值
        if (this.status !== PENDDING) { //判断执行状态是否是等待状态，等待则登出
            return
        }
        this.status = FUILEED //状态赋值，状态一旦确定不可更改
        this.value = value //回调参数赋值
        while (this.successCallback.length) {
            this.successCallback.shift()()
        }
        // this.successCallback && this.successCallback(this.value) //回调函数传参
    }
    reject = reason => { //失败的值（原因）
        if (this.status !== PENDDING) {
            return
        }
        this.status = REJECT
        this.reason = reason
        while (this.failCallback.length) {
            this.failCallback.shift()()
        }
        // this.failCallback && this.failCallback(this.reason)
    }
    static resolve(value) {
        if (value instanceof MyPromise) {
            return value
        } else {
            return new MyPromise(resolve => resolve(value))
        }
    }
    static all(arr) { //all()处理异步并发，限定执行顺序对应执行结果
        // 获取传入数组，定义结果容器，定义异步等待下标
        let result = []
        let index = 0;
        return new MyPromise((resolve, reject) => {
            function saveArr(key, value) {
                result[key] = value //存入执行结果于数组容器
                index++ //所有的遍历记录
                if (index == arr.length) { //当记录值与数组下标相等，表示遍历完毕数组所有值 
                    resolve(result) //回调这个返回数组结果的容器
                }
            }
            for (let i = 0; i < arr.length; i++) {
                // 遍历传参数组，判断每个当前值是否是promise对象
                // 如果是则当前值的回调函数结果存入数组容器（成功返回值，失败返回原因），
                // 如果不是当前值存入数组容器
                if (arr[i] instanceof MyPromise) {
                    arr[i].then(value => saveArr(i, value), reason => reject(reason))
                } else {
                    saveArr(i, arr[i])
                }
            }
        })
    }

    finally(callback) {
        // this.then:获取当前的promise执行状态
        return this.then(value => {
            // callback返回的值通过resolve转换为promise
            return MyPromise.resolve(callback()).then(() => value)
                // callback()
                //     // 下一个then，传参
                // return value
        }, reason => {
            return MyPromise.resolve(callback()).then(() => { throw reason })
                // callback()
                // return reason
        })
    }
    then(successCallback, failCallback) { //成功或者失败的回调处理
        // 判断thens是否参数可选:没有则返回这个函数，有则返回这个值
        successCallback = successCallback ? successCallback : value => value;
        failCallback = failCallback ? failCallback : reason => { throw reason }
            // 生成一个promsie对象，返回输出结果，可以访问对象的属性及方法
        let promiseEl = new MyPromise((resolve, reject) => {
            // 状态判断，差异化返还执行结果
            if (this.status == FUILEED) {
                setTimeout(() => {
                    try { //then回调错误捕获
                        // successCallback(this.value)获取第一次返回值
                        // resolve(resetValue):重新把return的值交给resolve处理
                        // 用于链式调用，return新值
                        // 由于此事件会在成功/失败/等待都会执行，因此封装
                        let resetValue = successCallback(this.value)
                            // console.log("reset", resetValue);   //reset 100
                            // resolve(resetValue)
                        resolvePromise(promiseEl, resetValue, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }

                }, 0);
            } else if (this.status == REJECT) {
                setTimeout(() => {
                    try {
                        let resetValue = failCallback(this.reason)
                        resolvePromise(promiseEl, resetValue, resolve, reject)
                    } catch (e) {
                        reject(e)
                    }
                }, 0);
            } else {
                //异步处理：当入参是一个异步值时，需要临时将其存入返还
                // 等待这两个函数被调用，执行resolve或者reject的successCallback&failCallback
                this.successCallback.push(() => {
                    setTimeout(() => {
                        try { //then回调错误捕获
                            // successCallback(this.value)获取第一次返回值
                            // resolve(resetValue):重新把return的值交给resolve处理
                            // 用于链式调用，return新值
                            // 由于此事件会在成功/失败/等待都会执行，因此封装
                            let resetValue = successCallback(this.value)
                                // console.log("reset", resetValue);   //reset 100
                                // resolve(resetValue)
                            resolvePromise(promiseEl, resetValue, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }

                    }, 0);
                })
                this.failCallback.push(() => {
                    setTimeout(() => {
                        try {
                            let resetValue = failCallback(this.reason)
                            resolvePromise(promiseEl, resetValue, resolve, reject)
                        } catch (e) {
                            reject(e)
                        }
                    }, 0);
                })
            }
        })
        return promiseEl
    }
    catchEl(failCallback) {
        // 传参为回调函数，通过then的两个处理方式（成功，失败），注册失败函数
        return this.then(undefined, failCallback)
    }


}

function resolvePromise(promiseEl, resetValue, resolve, reject) {
    if (promiseEl === resetValue) { //捕获自己调用自己的情况
        return reject(new TypeError('Chaining cycle detected for promise #<Promise>'))
    }
    if (resetValue instanceof MyPromise) {
        // resetValue.then(value => resolve(value), reason => reject(reason))
        resetValue.then(resolve, reject)
    } else {
        resolve(resetValue)
    }
}

module.exports = MyPromise